#include<iostream>
#include <pthread.h>
/*-------------------------------------自旋锁---------------------------------------------*/
/*
自旋锁：是指当一个线程在获取锁的时候，如果锁已经被其它线程获取，那么该线程将循环等待，然后不断的判断锁是否能够被成功获取，直到获取到锁才会退出循环
互斥锁：没有获取到锁的线程把自己阻塞起来，重新等待CPU的调度，这种锁称为互斥锁

自旋锁对比互斥锁的优点：
自旋锁尽可能的减少线程的阻塞，这对于锁的竞争不激烈，且占用锁时间非常短的代码块来说性能能大幅度的提升，因为自旋的消耗会小于线程阻塞挂起再唤醒的操作的消耗 ，这些操作会导致线程发生两次上下文切换！
非自旋锁在获取不到锁的时候会进入阻塞状态 ，从而进入内核态，当获取到锁的时候需要从内核态恢复，需要线程上下文切换。 
（线程被阻塞后便进入内核（Linux）调度状态，这个会导致系统在用户态与内核态之间来回切换，严重影响锁的性能）

自旋锁对比互斥锁的缺点：
锁的竞争激烈，或者持有锁的线程需要长时间占用锁执行同步块，自旋锁在获取锁前一直都是占用 cpu 做无用功，占着茅坑不拉屎
同时有大量线程在竞争一个锁，会导致获取锁的时间很长，线程自旋的消耗大于线程阻塞挂起操作的消耗，其它需要 cpu 的线程又不能获取到 cpu，造成 cpu 的浪费。

*/
/*-------------------------------------读写锁---------------------------------------------*/
/*
读写者问题：
在编写多线程的时候，有一种情况是比较常见的。那就是，有些公共数据修改的机会比较少。相较改写，它们读的机会反而多的多。
读者写者模式：三种关系，两类人，一个场所
三种关系：
    读者与读者：同步
    写者与写者：互斥
    读者与写者：同步与互斥
两类人：读者，写者
一个场所：同一临界资源（数据）

当不断有多个读者准备读时，如果有写者到来，此时应该让当前读者读完后，提高写者优先级，下个进入临界区的是写者。---------写者优先，否则会造成写者饥饿
当不断有多个写者准备写时，如果有读者到来，此时应该让当前写者写完后，提高读者优先级，下个进入临界区的是读者。---------读者优先，否则会造成读者饥饿

读写锁的内部包含两把锁：一把是读（操作）锁，是一种共享锁；另一把是写（操作）锁，是一种独占锁
在没有写锁的时候，读锁可以被多个线程同时持有
写锁被一个线程持有，其他的线程不能再持有写锁，抢占写锁会阻塞，抢占读锁也会阻塞
读写锁，可以对受保护的共享资源进行并发读取和独占写入

读者优先的伪代码理解rwlock实现原理：
//读者加锁 && 解锁
lock(&rlock);
reader count++;
if(reader count==1) lock(&wlock)://当出现第一个读者时，则将写者加锁，这样写者想写入时因拿不到锁而无法写入
unlock(&rlock)
// 进行读取
lock(&rlock);
reader count--:
if(reader_count==0) unlock(&wlock)://当没有读者时，释放写者的锁，这样写者拿到锁后就可以依次写入
unlock(&rlock)
*/
int main()
{
    /*自旋锁的使用*/
    //1.初始化
    pthread_spinlock_t slock;
    pthread_spin_init(&slock,0);
    //2.加锁
    pthread_spin_lock(&slock);
    //3.解锁
    pthread_spin_unlock(&slock);
    //4.释放锁
    pthread_spin_destroy(&slock);

    /*读写锁的使用*/
    //1.初始化
    pthread_rwlock_t r_w_lock;
    pthread_rwlock_init(&r_w_lock,0);
    /*
    第二个参数为0/nullptr时，函数会使用默认的读写锁属性
    读写锁属性：
    PTHREAD_RWLOCK_PREFER_READER_NP 读者将优先于写者获得锁（默认）
    PTHREAD_RWLOCK_PREFER_WRITER_NP 写者优先
    PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP 
    属性结合了写者优先的策略和非递归锁的特性。
    非递归锁意味着同一个线程不能多次获得同一个锁，否则会导致死锁。
    在这个属性下，写者会优先于读者获得锁，并且锁是非递归的，这有助于避免由于线程错误地多次获取锁而导致的死锁情况。

    ***注意***：是特定于某些操作系统（例如Solaris）的读写锁属性，它们不是POSIX标准的一部分，因此可能不是所有系统都支持这些属性
    */
   //修改读写锁的属性
    pthread_rwlockattr_t attr;
    pthread_rwlockattr_init(&attr);
    pthread_rwlockattr_setkind_np(&attr, PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP);
    pthread_rwlock_init(&r_w_lock, &attr);
    //2.加锁
    pthread_rwlock_rdlock(&r_w_lock);// 函数用于获取读锁
    /*
    当一个线程调用这个函数时，它会尝试获取读锁以读取共享资源。
    如果锁当前没有被任何线程持有（无论是读锁还是写锁），则该线程会立即获得读锁并继续执行。
    如果锁已经被一个或多个线程持有，但该锁是以读模式持有的（即有其他线程正在读取共享资源），则该线程也会获得读锁，因为多个线程可以同时读取共享资源。
    然而，如果锁是以写模式被持有的（即有一个线程正在修改共享资源），则调用 pthread_rwlock_rdlock() 的线程将被阻塞，直到写锁被释放
    */
    pthread_rwlock_wrlock(&r_w_lock);// 函数用于获取写锁
    /*
    当一个线程调用这个函数时，它会尝试获取写锁以修改共享资源。
    如果锁当前没有被任何线程持有，则该线程会立即获得写锁并继续执行。
    然而，如果锁已经被其他线程持有，无论是读锁还是写锁，调用 pthread_rwlock_wrlock() 的线程都将被阻塞，直到所有其他线程都释放了他们的锁。
    这是因为写操作需要独占访问共享资源，以防止其他线程同时读取或修改它
    */
    //3.解锁
    pthread_rwlock_unlock(&r_w_lock);
    //4.释放锁
    return 0;
}